//-------------------------------------------------------------------
//	Copyright (c) 2013  Zhirnov Andrey
//	This file is part of the "Noise Library" project.
//	See copyright notice in "linNoiseCL.h".
//-------------------------------------------------------------------

#pragma once

#include "../CSource.h"

namespace Noise
{
	
	//
	// Math Function Source with 1 argument
	//

	template <typename SrcFunc, ESource SourceType>
	class CMathSource1Arg : public CSource
	{
	// types
	protected:
		typedef CMathSource1Arg< SrcFunc, SourceType >	Self;


	// variables
	protected:
		CSourcePtr	_value;
		
		static const
			ESource	_sourceType = SourceType;

	// methods
	public:
		CMathSource1Arg ()
		{}

		~CMathSource1Arg ()
		{}

		ESourceState GetState () const
		{
			return _value.IsNotNull() ? SOURCE_COMPLETE : UNCOMPLETE_ATTACHMENT_SOURCE;
		}
		
		virtual void Delete ()
		{
			_value = null;
		}

		virtual EResult GetInteger (ESourceQuery type, int *v, int count) const
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case SOURCE_STATE :
					v[0] = GetState();
					return OK;

				case SOURCE_TYPE :
					v[0] = _sourceType;
					return OK;

				case SOURCES_COUNT :
				{
					int	count = 0;
					_value->GetInteger( SOURCES_COUNT, &count, 1 );

					v[0] = count + 1;
					return OK;
				}

				case INPUT_SOURCES :
					v[0] = 1;
					return OK;
			}
			return INVALID_ENUM;
		}

		virtual EResult SetArg (EArgument type, CSourcePtr src)
		{
			switch ( type )
			{
				case SOURCE_0 :	_value = src;	return OK;
			}
			return INVALID_ENUM;
		}

		virtual EResult GetArg (EArgument type, CSourcePtr &src) const
		{
			switch ( type )
			{
				case SOURCE_0 :	src = _value;	return OK;
			}
			return INVALID_ENUM;
		}
		
		virtual CSourcePtr Clone () const override
		{
			return new Self();
		}

		virtual void PreBuild (const TPreBuildData &data) override
		{
			data.Push( _value.ptr() );
		}

		virtual bool BuildFunction (TFunctionData &data) override
		{
			CSource::BuildFunctionHeader( data.source, _id );
			data.source << " ( ";
			SrcFunc::Build( data, _value->GetSourceId() );
			data.source << " )\n";
			return true;
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		Real NativeFunction (const TVec<Real,3> &vPoint)
		{
			return SrcFunc::Native< Real, NativeFunction >( vPoint, _value );
		}
		
		virtual float32 NativeFunction32 (const fvec3 &vPoint) override
		{
			return NativeFunction< float32, &CSource::NativeFunction32 >( vPoint );
		}

		virtual float64 NativeFunction64 (const dvec3 &vPoint) override
		{
			return NativeFunction< float64, &CSource::NativeFunction64 >( vPoint );
		})

		static bool CreateSource (CSourcePtr &src, CContext *context, ESource type)
		{
			ASSERT( type == _sourceType );

			src = new Self();
			return true;
		}
	};



	//
	// Abs
	//

	struct Math_Abs
	{
		static void Build (CSource::TFunctionData &data, uint src)
		{
			data.source << "fabs( ";
			CSource::BuildFunctionCall( data.source, src );
			data.source << " )";
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr src)
		{
			return Abs( (src.ptr()->*NativeFunction)( vPoint ) );
		})
	};

	typedef CMathSource1Arg< Math_Abs, ABS >	CSourceAbs;



	//
	// Inv
	//

	struct Math_Inv
	{
		static void Build (CSource::TFunctionData &data, uint src)
		{
			data.source << "-";
			CSource::BuildFunctionCall( data.source, src );
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr src)
		{
			return -(src.ptr()->*NativeFunction)( vPoint );
		})
	};

	typedef CMathSource1Arg< Math_Inv, INV >	CSourceInv;



	//
	// Exp
	//

	struct Math_Exp
	{
		static void Build (CSource::TFunctionData &data, uint src)
		{
			data.source << "exp( ";
			CSource::BuildFunctionCall( data.source, src );
			data.source << " )";
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr src)
		{
			return Exp( (src.ptr()->*NativeFunction)( vPoint ) );
		})
	};

	typedef CMathSource1Arg< Math_Exp, EXP >	CSourceExp;



	//
	// Exp2
	//

	struct Math_Exp2
	{
		static void Build (CSource::TFunctionData &data, uint src)
		{
			data.source << "exp2( ";
			CSource::BuildFunctionCall( data.source, src );
			data.source << " )";
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr src)
		{
			return Exp2( (src.ptr()->*NativeFunction)( vPoint ) );
		})
	};

	typedef CMathSource1Arg< Math_Exp2, EXP2 >	CSourceExp2;



	//
	// Exp10
	//

	struct Math_Exp10
	{
		static void Build (CSource::TFunctionData &data, uint src)
		{
			data.source << "exp10( ";
			CSource::BuildFunctionCall( data.source, src );
			data.source << " )";
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr src)
		{
			return Exp10( (src.ptr()->*NativeFunction)( vPoint ) );
		})
	};

	typedef CMathSource1Arg< Math_Exp10, EXP10 >	CSourceExp10;



	//
	// Log
	//

	struct Math_Log
	{
		static void Build (CSource::TFunctionData &data, uint src)
		{
			data.source << "log( ";
			CSource::BuildFunctionCall( data.source, src );
			data.source << " )";
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr src)
		{
			return Log( (src.ptr()->*NativeFunction)( vPoint ) );
		})
	};

	typedef CMathSource1Arg< Math_Log, LOG >	CSourceLog;



	//
	// Log2
	//

	struct Math_Log2
	{
		static void Build (CSource::TFunctionData &data, uint src)
		{
			data.source << "log2( ";
			CSource::BuildFunctionCall( data.source, src );
			data.source << " )";
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr src)
		{
			return Log2( (src.ptr()->*NativeFunction)( vPoint ) );
		})
	};

	typedef CMathSource1Arg< Math_Log2, LOG2 >	CSourceLog2;



	//
	// Log10
	//

	struct Math_Log10
	{
		static void Build (CSource::TFunctionData &data, uint src)
		{
			data.source << "log10( ";
			CSource::BuildFunctionCall( data.source, src );
			data.source << " )";
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr src)
		{
			return Log10( (src.ptr()->*NativeFunction)( vPoint ) );
		})
	};

	typedef CMathSource1Arg< Math_Log10, LOG10 >	CSourceLog10;



	//
	// ScaleBias
	//

	struct Math_ScaleBias
	{
		static void Build (CSource::TFunctionData &data, uint src) {}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		static Real Native (const TVec<Real,3> &vPoint, CSourcePtr src) { return Real(0); })
	};

	class CSourceScaleBias : CMathSource1Arg< Math_ScaleBias, SCALE_BIAS >
	{
	// variables
	protected:
		real	_scale;
		real	_bias;

	// methods
	public:
		CSourceScaleBias () : _scale(1.0f), _bias(0.0f) {}
		
		virtual EResult SetArg (EArgument type, const real *v, int count)
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case ARG_SCALE :	_scale = v[0];	return OK;
				case ARG_BIAS  :	_bias  = v[0];	return OK;
			}
			return INVALID_ENUM;
		}
		
		virtual EResult GetArg (EArgument type, real *v, int count) const
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case ARG_SCALE :	v[0] = _scale;	return OK;
				case ARG_BIAS  :	v[0] = _bias;	return OK;
			}
			return INVALID_ENUM;
		}
		
		virtual CSourcePtr Clone () const override
		{
			CSourceScaleBias *	src = new CSourceScaleBias();
			src->_scale = _scale;
			src->_bias	= _bias;
			return src;
		}

		virtual bool BuildFunction (TFunctionData &data) override
		{
			CSource::BuildFunctionHeader( data.source, _id );
			data.source << " ( mad( ";
			CSource::BuildFunctionCall( data.source, _value->GetSourceId() );
			data.source << ", " << _scale << ", " << _bias << " ) )\n";

			return true;
		}
		
		NATIVE(
		template < typename Real, Real (CSource:: *NativeFunction)(const TVec<Real,3> &) >
		Real NativeFunction (const TVec<Real,3> &vPoint)
		{
			return (_value.ptr()->*NativeFunction)( vPoint ) * _scale + _bias;
		}
		
		virtual float32 NativeFunction32 (const fvec3 &vPoint) override
		{
			return NativeFunction<float32, &CSource::NativeFunction32>( vPoint );
		}

		virtual float64 NativeFunction64 (const dvec3 &vPoint) override
		{
			return NativeFunction<float64, &CSource::NativeFunction64>( vPoint );
		})

		static bool CreateSource (CSourcePtr &src, CContext *context, ESource type)
		{
			ASSERT( type == _sourceType );

			src = new CSourceScaleBias();
			return true;
		}
	};



}	// Noise